######################################################################
# Time-stamp: <05/08/09 16:28:19 ostolop>
#
# General Dynamic Data Routines and Structures
######################################################################
package EP::EPC::Interface::DD;
use strict;

use XML::LibXML;
use EP::Config;

use EP::Common::DBI;
use EP::Common::DBI::Dataset;

sub session_id {
    my ($query) = new CGI;

    my %cookie = $query->cookie($EP::Config::h->{SESSION_COOKIE_NAME} );
    return $cookie{session_id};
}

my $dynamic_data_XML = <<__DynXML__;
<?xml version="1.0"?><dynamic_data/>
__DynXML__

sub dynamic_list {
  my ($list) = @_;

  my $doc;
  my $parser = XML::LibXML->new();
  $doc = $parser->parse_string ( $dynamic_data_XML );

  my $root = $doc->getDocumentElement();

  foreach ( sort { $a <=> $b } keys %$list ) {
    $root->appendChild ( create_dynamic_list_item ( $doc, $list->{$_} ) );
  }

  return $doc;
}

sub create_dynamic_list_item {
  my ($doc, $list_item_hash) = @_;
  my ($value_select);

  my $list_item = $doc->createElement ( "item" );

  foreach my $item_attr_name ( keys %$list_item_hash ) {
    # for value put it into text
    if ( $item_attr_name =~ /value/) {
      my $value_text = $doc->createTextNode ( $list_item_hash->{value} );
      $list_item->appendChild ( $value_text );
      next;
    }

    my $item_attr_value = "";

    $_ = ref ( $list_item_hash->{$item_attr_name} );

    if (not $_) {
      $item_attr_value = $list_item_hash->{$item_attr_name};
    } else {
    SWITCH: {
	/ARRAY/
	  && do {
	    $item_attr_value = join ( "; ", @{$list_item_hash->{$item_attr_name}} );
	};
	
	/HASH/
	  && do {
	    foreach ( keys %{$list_item_hash->{$item_attr_name}} ) {
	      $item_attr_value .= "; " if not $item_attr_value eq "";
	      $item_attr_value .= "$_| " . $list_item_hash->{$item_attr_name}->{$_};
	    };
	  }
	}
    }

    $list_item->setAttribute ( $item_attr_name, $item_attr_value );
  }

  return $list_item;
}

1;

######################################################################
# Time-stamp: <2003-10-21 16:53:47 patrick>
#
# Routines to call for dynamic data retrieval from extended xsl
######################################################################
package EP::EPC::Interface::DDDispatcher;
use strict;

use Text::Template;

use EP::EPC::Interface::Data_Upload;
use EP::EPC::Interface::Data_Selection;
use EP::EPC::Interface::Hierarchical_Clustering;
use EP::EPC::Interface::Clustering_K_groups;
use EP::EPC::Interface::Between_Group_Analysis;
use EP::EPC::Interface::Ordination;
use EP::EPC::Interface::Data_Transformation;
use EP::EPC::Interface::Published_Datasets;
use EP::EPC::Interface::Clustering_Comparison;
use EP::EPC::Interface::Sequence_Motif;
use EP::EPC::Interface::GO_Annotation;
use EP::EPC::Interface::User_Preferences;

=head1 NAME

EP_XSL_Extensions::DDDispatcher - dynamic data dispatcher, organized
by component ID, containing references to all the dynamic data subs

=head1 SYNOPSIS

C<our $DDD = EP_XSL_Extensions::DDDispatcher->new ( $ep_query_doc );>

Here C<$ep_query_doc> is an XML::LibXML document object, containing
the EP query.

=head1 DESCRIPTION

=head1 METHODS

=over

=item new ( epc_query )

Creates a new Dispatcher object, initializing it with the passed EPC
query XML document. The EPC query document is needed for dynamically
identifying active subsections on component chaining.

=back

=cut

sub new {
  my $proto = shift;
  my ($ep_query_doc, $session) = @_;
  my $class = ref($proto) || $proto;
  my $self  = {};
  bless $self, $class;

  $self->initialize($ep_query_doc, $session);
  return $self;
}

sub initialize {
  my $self = shift;
  my ($ep_query_doc, $session) = @_;

  $self->{session} = $session;
  $self->{ep_query_doc} = $ep_query_doc;

  $self->{1}  = EP::EPC::Interface::Data_Upload->            newDD ( $ep_query_doc, $session );
  $self->{2}  = EP::EPC::Interface::Data_Selection->         newDD ( $ep_query_doc, $session, $self );
  $self->{3}  = EP::EPC::Interface::Hierarchical_Clustering->newDD ( $ep_query_doc, $session, $self );
  $self->{4}  = EP::EPC::Interface::Clustering_K_groups->    newDD ( $ep_query_doc, $session, $self );
  $self->{6}  = EP::EPC::Interface::Between_Group_Analysis-> newDD ( $ep_query_doc, $session, $self );
  $self->{7}  = EP::EPC::Interface::Ordination->             newDD ( $ep_query_doc, $session, $self );
  $self->{8}  = EP::EPC::Interface::Data_Transformation->    newDD ( $ep_query_doc, $session, $self );
  $self->{10} = EP::EPC::Interface::Published_Datasets->     newDD ( $ep_query_doc, $session, $self );
  $self->{13} = EP::EPC::Interface::Clustering_K_groups->    newDD ( $ep_query_doc, $session, $self );
  $self->{14} = EP::EPC::Interface::Sequence_Motif->         newDD ( $ep_query_doc, $session, $self );
  $self->{26} = EP::EPC::Interface::GO_Annotation->          newDD ( $ep_query_doc, $session, $self );
  $self->{17} = EP::EPC::Interface::User_Preferences->       newDD ( $ep_query_doc, $session, $self );

  return;
}

=over

=item dynamic_call ( comp_id, sub_name, arg )

Dynamic call will search the top level for a function definition of
C<sub_name>, and if it exists, it will be called, unless the component
C<comp_id> also defines it, in which case that component's function
will be called. The C<arg> is the argument passed to the function.
'

=back

=cut

sub dynamic_call {
  my $self = shift;
  my $comp_id = shift;
  my $sub_name = shift;
  my @arg = @_;

  if ( UNIVERSAL::can ( $self->{$comp_id}, $sub_name ) ) {
    return $self->{$comp_id}->$sub_name ( @arg );
  }

  if ( UNIVERSAL::can ( $self, $sub_name ) ) {
    return $self->$sub_name ( @arg );
  }

  die "[DDCALL] Subroutine $sub_name not defined [EPC $comp_id requested, ARG: @arg]!\n";
}

sub get_folder_tree {
  my $self = shift;

  return EP::Common::DB::get_folder_tree ( $self->{session} );
}

sub get_current_analysis_history_tree {
  my $self = shift;

  return EP::Common::DB::get_analysis_history ( $self->{session} );
}

sub get_data_tree {
  my $self = shift;

  my $username      = $self->{session}->owner->username;
  my $cur_folder_id = $self->{session}->current_folder->id;
  my $cur_root_dsid = $self->{session}->current_root_dataset->id if $self->{session}->current_root_dataset;
  my $cur_actid     = $self->{session}->current_action->id if $self->{session}->current_action;
  my $session_id    = $self->{session}->id;

  return EP::Common::DB::get_data_tree ( $username, $session_id, $cur_folder_id, $cur_root_dsid, $cur_actid );
}

sub get_arrayexpress_experiments {
  my $self = shift;

  return EP::Common::DB::get_arrayexpress_experiments ();
}

sub get_session_id {
  my $self = shift;

  return $self->{session}->id;
}

################################################################################
## WIP ##
################################################################################
sub get_current_folder_id {
    my $self = shift;
    my $username = $self->get_current_username();

    my $cur_folder_id = EP::Common::DB::get_user_configuration( $username, "current_folder_id" );

    if ( $self->{session}->{current_folder_id} and
	 $self->{session}->{current_folder_id} > 0 and
	 $self->{session}->{current_folder_id} != $cur_folder_id ) {
	
	# extra check in case folder was deleted ( might be improved by just resetting the session current_folder_id )
	if ( EP::Common::DB::get_folder( { id => $self->{session}->{current_folder_id} } ) ) {
	    $cur_folder_id = $self->{session}->{current_folder_id};
	    EP::Common::DB::set_user_configuration ( $username, "current_folder_id", $cur_folder_id );

 	    $self->{session}->{current_root_dataset_id} = EP::Common::DB::get_latest_dataset_id_in_folder ( $cur_folder_id );
	    EP::Common::DB::set_user_configuration ( $username, "current_root_dataset_id", $self->{session}->{current_root_dataset_id} );

	    $self->{session}->{current_action_id} = undef;
	    EP::Common::DB::set_user_configuration ( $username, "current_action_id", undef );
	}
    }

    return $cur_folder_id;
}

sub get_current_root_dataset_id {
  my $self = shift;
  my $username = $self->get_current_username ();
  my $cur_root_dsid = EP::Common::DB::get_user_configuration ( $username, "current_root_dataset_id" );

  if ( $self->{session}->{current_root_dataset_id} and
       $self->{session}->{current_root_dataset_id} > 0 and
       $self->{session}->{current_root_dataset_id} < 2147483647 and # this is rather silly - we need to reset session
                                                                    # current_root_dataset_id so that they don't get
                                                                    # concatenated when deleting many datasets (eg)
       $self->{session}->{current_root_dataset_id} != $cur_root_dsid ) {

      # extra check in case root_dataset was deleted ( might be improved by just resetting the session current_root_dataset_id )
      if ( EP::Common::DB::get_dataset( { id => $self->{session}->{current_root_dataset_id} } ) ) {
	  $cur_root_dsid = $self->{session}->{current_root_dataset_id};
	  EP::Common::DB::set_user_configuration ( $username, "current_root_dataset_id", $cur_root_dsid );
      }
  }

  return $cur_root_dsid;
}

sub get_current_action_id {
  my $self = shift;
  my $username = $self->get_current_username ();
  my $cur_action_id = EP::Common::DB::get_user_configuration ( $username, "current_action_id" );

  if ( !$self->{session}->{is_epc_active} and
       $self->{session}->{current_action_id} and
       $self->{session}->{current_action_id} > 0 and
       $self->{session}->{current_action_id} != $cur_action_id ) {

      # extra check in case root_dataset was deleted ( might be improved by just resetting the session current_root_dataset_id )
      if ( EP::Common::DB::get_action_dst_dataset( $self->{session}->{current_action_id} ) ) {
	  $cur_action_id = $self->{session}->{current_action_id};
	  EP::Common::DB::set_user_configuration ( $username, "current_action_id", $cur_action_id );
      }
  }

  return $cur_action_id;
}
################################################################################
## END WIP ##
################################################################################

sub get_username {
  my $self = shift;

  my $username = $self->{session}->owner->username;

  die '%session not initialized: no username attribute!' if not $username;
  return $username;
}

sub get_current_username {
  my $self = shift;
  return $self->get_username ();
}

sub get_current_folder {
  my $self = shift;
  return $self->get_username ();
}

sub parse_template {
    my $self = shift;
    my ($tmpl_values, $src, $dst) = @_;
    local(*FILE);

    my $template = Text::Template->new(TYPE => "FILE", SOURCE => $src );
    my $text = $template->fill_in( HASH => $tmpl_values );

    open (FILE, ">$dst");
    print FILE $text;
    close FILE;

    return 1;
}

sub gui_link_javascript {
  my $self = shift;
  my ($via_call, $via_comp_id, $from, $from_call, $from_arg) = @_;

  $from =~ /___c_(.*?)_/;
  my $from_comp_id = $1;

  # $from_list will be a dynamic_data xml structure - one node "<dynamic_data>" will contain the list
  my $from_list = $self->{$from_comp_id}->$from_call ( $from_arg );

  my $doc = $from_list->get_node(1)->ownerDocument();
  my @from_items = $from_list->get_node(1)->findnodes("item")->get_nodelist();

  foreach my $from_node ( @from_items ) {
    print STDERR "[GUI LINK] Calling $via_call ( ",  $from_node->findvalue('@key') , ") ... \n" if $EP::Config::h->{DEBUG};
    # via call returns a reference to a hash where the keys are the
    # @item_name's from the $to NodeSet.
    my $link_values = $self->{$via_comp_id}->$via_call ( $from_node->findvalue('@key') );

    foreach my $link_value_name ( keys (%{$link_values}) ) {
      my $link_value_item = $doc->createElement( "link_value" );
      $link_value_item->setAttribute ( "name", $link_value_name );
      $link_value_item->setAttribute ( "type", $link_values->{$link_value_name}->{type} );
      my $link_value_text = $doc->createTextNode ( $link_values->{$link_value_name}->{value} );
      $link_value_item->appendChild ( $link_value_text );

      $from_node->appendChild ( $link_value_item );
    }
  }

  return $from_list;
}

######################################################################
# lists all the datasets in a particular folder
######################################################################
sub list_datasets {
  my $self = shift;

  my $datasets = EP::Common::DBI::Dataset->search ( { folder  => $self->{session}->current_folder,
						      ep_session => $self->{session}->owner->session_only ? $self->{session}->id : undef
						    } );
  my $current_dataset = $self->{session}->current_root_dataset;

  my $D;

  while ( my $dataset = $datasets->next ) {
    next if not $dataset->name;

    $D->{$dataset->id}->{href} = $EP::Config::h->{EPNG_USRDATA_URL} . "/" . $dataset->folder->location . "/" . $dataset->filename;
    $D->{$dataset->id}->{row}  = "<a target=\"_blank\" href=\"" . $EP::Config::h->{EPNG_USRDATA_URL} . "/" . $dataset->folder->location . "/" . $dataset->filename . ".rows\">rows</a>";
    $D->{$dataset->id}->{col}  = "<a target=\"_blank\" href=\"" . $EP::Config::h->{EPNG_USRDATA_URL} . "/" . $dataset->folder->location . "/" . $dataset->filename . ".columns\">cols</a>";
    $D->{$dataset->id}->{is_current} = ( defined $current_dataset and $dataset->id == $current_dataset->id );

    $D->{$dataset->id}->{key} = $dataset->id;
    $D->{$dataset->id}->{value} = $dataset->name;
  }

  my $dl = EP::EPC::Interface::DD::dynamic_list ( $D );
  return $dl->findnodes('/*');
}

######################################################################
# lists available sources of data (folders). right now, hard-coded to
# only list the default folder (corresponding to the username)
######################################################################
sub list_available_sources {
  my $self = shift;

  my $datasets = { 0 => { key => $self->get_username(),
			  value => "default folder" }
		 };

  my $dl = &EP::EPC::Interface::DD::dynamic_list ( $datasets );
  return $dl->findnodes('/*');
}

sub list_folders {
  my $self = shift;

  my $folders = { 0 => { key => EP::Common::DB::get_folder_id ( $self->get_username() ),
			 value => "the default folder",
			 is_current => 1
		       }
		 };

  my $dl = &EP::EPC::Interface::DD::dynamic_list ( $folders );
  return $dl->findnodes('/*');
}

1;
